package test

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"strings"
	"testing"

	"gitee.com/wx-rdc/go-test/pkg/conv"
	"gitee.com/wx-rdc/go-test/pkg/jsonReplacer"
	"gitee.com/wx-rdc/go-test/pkg/rand"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/unknwon/com"
)

type TestCase struct {
	testing.InternalTest
	Method        string //请求类型
	Url           string //链接
	ContentType   string //
	Authorization string
	Param         string      //参数
	Code          int         //状态码
	Desc          string      //描述
	ShowBody      bool        //是否展示返回
	ErrMsg        string      //错误信息
	Ext1          interface{} //自定义1
	Ext2          interface{} //自定义2
}

type InitCallback func() (*gin.Engine, error)

type TestCallback struct {
	Init InitCallback
}

var route *gin.Engine
var caseList []TestCase

var callbackFuncs = TestCallback{
	defaultInitCallback,
}

func defaultInitCallback() (*gin.Engine, error) {
	return nil, errors.New("default init callback")
}

func RegisterInitCallback(cb InitCallback) {
	callbackFuncs.Init = cb
}

func getInternalVars(name string) string {
	if name == "rand" {
		r := rand.RandStringBytes(6)
		return r
	}
	return name
}

func NewBufferString(body string) io.Reader {
	return bytes.NewBufferString(body)
}

func MergeMap(mObj ...map[string]interface{}) map[string]interface{} {
	newObj := make(map[string]interface{})
	for _, m := range mObj {
		for k, v := range m {
			newObj[k] = v
		}
	}
	return newObj
}

func MakeGetRequest(uri string, param url.Values) (r *http.Request) {
	u := uri
	if len(param) > 0 {
		u = u + "?" + param.Encode()
	}
	r = httptest.NewRequest("GET", u, nil)
	return
}

func MakePostRequest(uri string, param url.Values) (r *http.Request) {
	var payload io.Reader
	if len(param) > 0 {
		payload = strings.NewReader(param.Encode())
	}
	r = httptest.NewRequest("POST", uri, payload)
	return
}

func MakePostJSONRequest(uri string, data map[string]interface{}) (r *http.Request) {
	var payload io.Reader
	if len(data) > 0 {
		d, _ := json.Marshal(data)
		payload = NewBufferString(string(d))
	}
	r = httptest.NewRequest("POST", uri, payload)
	return
}

// 具体的每一个请求
func DoRequest(ts *TestCase, vars map[string]interface{}) (c *gin.Context, r *http.Request, w *httptest.ResponseRecorder) {
	if ts.Method == "" {
		ts.Method = "GET"
	}
	w = httptest.NewRecorder()
	c, _ = gin.CreateTestContext(w)

	// getValueByName := func(k string, dv interface{}) interface{} {
	// 	v := dv
	// 	if strings.HasPrefix(k, "$$") {
	// 		v = getInternalVars(k[2:])
	// 	} else if strings.HasPrefix(k, "$") {
	// 		vv := vars
	// 		for _, s := range strings.Split(k[1:], ".") {
	// 			if reflect.ValueOf(vv[s]).Kind() == reflect.Map {
	// 				vv = vv[s].(map[string]interface{})
	// 			} else {
	// 				v = vv[s]
	// 				break
	// 			}
	// 		}
	// 	}
	// 	return v
	// }

	getValueByName := func(i interface{}) interface{} {
		v := i
		val, ok := i.(string)
		if ok {
			if strings.HasPrefix(val, "$$") {
				v = getInternalVars(val[2:])
			} else if strings.HasPrefix(val, "$") {
				vv := vars
				for _, s := range strings.Split(val[1:], ".") {
					t := vv[s]
					arr := strings.Split(s, "#")
					if len(arr) > 1 {
						elist := vv[arr[0]].([]interface{})
						pos := com.StrTo(arr[1]).MustInt()
						t = elist[pos]
					}
					if reflect.ValueOf(t).Kind() == reflect.Map {
						vv = t.(map[string]interface{})
					} else {
						v = t
						break
					}
				}
			}
		}
		return v
	}
	replacer := jsonReplacer.NewReplacer(getValueByName,
		jsonReplacer.WithKind(reflect.Invalid),
		jsonReplacer.WithKeywords([]string{}))
	// ret := r.Replacing([]byte(`{"name1":"Bob","name2":"Alice"}`))
	// fmt.Println(string(ret))	// {"name1":"hello Bob","name2":"hello Alice"}

	dUrl := make(map[string]interface{})
	dUrl["url"] = strings.Split(ts.Url, "/")
	dUrlBytes, _ := json.Marshal(dUrl)

	urlRet, _ := replacer.Replacing(dUrlBytes)
	json.Unmarshal([]byte(string(urlRet)), &dUrl)

	out := []string{}
	for _, v := range dUrl["url"].([]interface{}) {
		out = append(out, v.(string))
	}
	realUrl := strings.Join(out, "/")
	fmt.Println(realUrl)

	ret, _ := replacer.Replacing([]byte(ts.Param))
	d := make(map[string]interface{})
	json.Unmarshal([]byte(string(ret)), &d)

	if ts.Method == "GET" {
		param := make(url.Values)
		for k, v := range d {
			param[k] = []string{conv.StrTo(v)}
		}
		if ts.ShowBody && len(param) > 0 {
			fmt.Println(param.Encode())
		}
		r = MakeGetRequest(realUrl, param)
	} else {
		if strings.Index(ts.ContentType, "json") > 0 {
			// dataMap := make(map[string]interface{})
			// json.Unmarshal([]byte(ts.Param), &dataMap)
			// d := make(map[string]interface{})
			// for k, v := range dataMap {
			// 	d[k] = getValueByName(fmt.Sprint(v), v)
			// }
			if ts.ShowBody && len(d) > 0 {
				b, _ := json.Marshal(d)
				fmt.Println(string(b))
			}
			r = MakePostJSONRequest(realUrl, d)
		} else {
			// dataMap := make(map[string]interface{})
			// json.Unmarshal([]byte(ts.Param), &dataMap)
			// param := make(url.Values)
			// for k, v := range dataMap {
			// 	s := fmt.Sprint(getValueByName(fmt.Sprint(v), v))
			// 	param[k] = []string{s}
			// }
			// if ts.ShowBody && len(param) > 0 {
			// 	fmt.Println(param.Encode())
			// }
			param := make(url.Values)
			for k, v := range d {
				param[k] = []string{conv.StrTo(v)}
			}
			if ts.ShowBody && len(param) > 0 {
				fmt.Println(param.Encode())
			}
			r = MakePostRequest(realUrl, param)
		}
	}
	c.Request = r
	c.Request.Header.Set("Content-Type", ts.ContentType)
	c.Request.Header.Set("GINAPPKEY", "100000")
	if ts.Authorization != "" {
		auth := fmt.Sprint(getValueByName(ts.Authorization))
		c.Request.Header.Set("Authorization", "Bearer "+auth)
	}

	route.ServeHTTP(w, r)
	return
}

func matchAll(pat string, str string) (bool, error) {
	return true, nil
}

func findTestCaseByName(name string) TestCase {
	for _, v := range caseList {
		if v.Desc == name {
			return v
		}
	}
	return TestCase{}
}

// 执行 测试
func Exec(t *testing.T, cs []TestCase) {
	ExecWithVars(t, cs, nil)
}

// 执行 测试
func ExecWithVars(t *testing.T, cs []TestCase, vars map[string]interface{}) {
	_route, err := callbackFuncs.Init()
	if err != nil {
		t.Fatalf("%v", err)
	}
	route = _route

	caseList = cs

	var ts []testing.InternalTest

	var d map[string]interface{}
	if vars != nil {
		d = MergeMap(vars, d)
	}

	for _, v := range caseList {
		v.Name = v.Desc
		v.F = func(t *testing.T) {
			p := findTestCaseByName(t.Name())
			_, _, w := DoRequest(&p, d)
			// fmt.Printf("测试用例%d: %s\n", k+1, v.Desc)
			if p.ShowBody {
				fmt.Printf("%s\n", w.Body.String())
			}
			dataMap := make(map[string]interface{})
			json.Unmarshal(w.Body.Bytes(), &dataMap)
			lastData := dataMap["data"]
			if lastData != nil {
				d = MergeMap(lastData.(map[string]interface{}), d)
			}
			assert.Equal(t, 200, w.Code)
		}
		ts = append(ts, v.InternalTest)
		//result := Result(w.Body.String())
		//fmt.Println("接口返回:", w.Body.String())
		//assert.Equal(t, "success", result["msg"])
	}

	testing.Init()
	// testing.MainStart(matchStringOnly(matchAll), ts, nil, nil, nil).Run()
	assert.True(t, testing.RunTests(matchAll, ts))
}
